package de.unima.alcomox.mapping;





import java.text.DecimalFormat;




/**
* Characterises the relation between two Alignments in terms of recall, precision and f-value.
*/
public class Characteristic {
	
	private int numOfRulesGold;
	private int numOfRulesMatcher;
	private int numOfRulesCorrect;
	
	private static boolean strictEvaluation = true;;
	
	/**
	* Constructs an empty characteristic which is a characteristic for an Alignment of cardinality zero. 
	*
	*/
	public Characteristic() {
		this(0,0,0);
	}
	
	/**
	* Constructs a characteristic.
	* 
	* @param numOfRulesGold Number of correspondences of the reference Alignment.
	* @param numOfRulesMatcher Number of correspondences in the Alignment under discussion
	* (in most cases the Alignment generated by a matching system). 
	* @param numOfRulesCorrect Number of correspondences that are both the reference Alignment and the 
	* generated Alignment.
	*/
	protected Characteristic(int numOfRulesGold, int numOfRulesMatcher, int numOfRulesCorrect) {
		this.numOfRulesGold = numOfRulesGold;
		this.numOfRulesMatcher = numOfRulesMatcher;
		this.numOfRulesCorrect = numOfRulesCorrect;
	}
	
	/**
	* Constructs a characteristic based by comparing two Alignments.
	* 
	* @param Alignment The Alignment under discussion.
	* @param reference The reference Alignment.
	* @throws ALCOMOException Thrown if the namespaces of the Alignments differ.
	*/
	public Characteristic(Alignment Alignment, Alignment reference) {
		// Alignment correct = reference.getIntersection(Alignment); 
		Alignment correct = new Alignment();
		if (strictEvaluation) {
			for (Correspondence r : reference) {
				for (Correspondence m : Alignment) {
					if (m.equals(r)) {
						correct.push(r);
					}
				}
			}
		}
		else {
			for (Correspondence r : reference) {
				for (Correspondence m : Alignment) {
					if (m.getSourceEntityUri().equals(r.getSourceEntityUri()) && m.getTargetEntityUri().equals(r.getTargetEntityUri())) {
						correct.push(r);
					}
				}
			}			
		}
		
		this.numOfRulesGold = reference.size();
		this.numOfRulesMatcher = Alignment.size();
		this.numOfRulesCorrect = correct.size();
	}	
	
	/**
	* Joins this Alignment with another Alignment by summing up relevant characteristics
	* in absolute numbers. Larger matching problems are thus to a greater extent weighted.
	*  
	* @param c The other charcteristic.
	*/
	public void join(Characteristic c) {
		this.numOfRulesCorrect += c.getNumOfRulesCorrect();
		this.numOfRulesGold += c.getNumOfRulesGold();
		this.numOfRulesMatcher += c.getNumOfRulesMatcher();
	}
	
	/**
	* Returns a string representation. 
	* 
	* @return A string representation.
	*/
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Precision: " + (100.0 * this.getPrecision()) + "%\n");
		sb.append("Recall:    " + (100.0 * this.getRecall()) + "%\n");
		sb.append("F-measure: " + (100.0 * this.getFMeasure()) + "%\n");
		sb.append("Gold: " + this.numOfRulesGold + " Matcher: " + numOfRulesMatcher +  " Correct: " + numOfRulesCorrect + "\n");
		return sb.toString();
	}

	/**
	* Returns the f-measure.
	* @return The f-measure.
	*/
	public double getFMeasure() {
		if ((this.getPrecision() == 0.0f) || (this.getRecall() == 0.0f)) { return 0.0f; }
		return (2 * this.getPrecision() * this.getRecall()) / (this.getPrecision() + this.getRecall());
	}

	/**
	* Returns the precision.
	* 
	* @return The precision.
	*/
	public double getPrecision() {
		return (double)this.numOfRulesCorrect /  (double)this.numOfRulesMatcher;
	}
	
	/**
	* Returns the recall.
	* 
	* @return The recall.
	*/
	public double getRecall() {
		return (double)this.numOfRulesCorrect /  (double)this.numOfRulesGold;
	}

	
	public int getNumOfRulesCorrect() {
		return numOfRulesCorrect;
	}

	public int getNumOfRulesGold() {
		return numOfRulesGold;
	}

	public int getNumOfRulesMatcher() {
		return numOfRulesMatcher;
	}

	public String toShortDesc() {
		double precision = this.getPrecision();
		double recall = this.getRecall();
		double f = this.getFMeasure();

		return toDecimalFormat(precision) + "\t" + toDecimalFormat(recall) + "\t" + toDecimalFormat(f);
	}

	private static String toDecimalFormat(double precision) {
		DecimalFormat df = new DecimalFormat("0.000");
		return df.format(precision).replace(',', '.');
	}

	public static void useDiffuseEvaluation() {
		strictEvaluation = false;
		
	}

	public static boolean strictEvaluationActive() {
		return strictEvaluation;
	}






	
}
